﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Drawing;

namespace Client
{
    public class Client
    {
        public delegate void FormMessageDelegate(string msg, Color color, int auctionID);
        public delegate void FormUpdateAuctions();
        private TcpClient server;
        private NetworkStream stream;
        private FormAuctionClient formAuctionClient;
        private Reader reader;
        private StreamWriter writer;
        private List<Auction> auctions;
        private string servername, myIP;
        private int port;
        private Thread readerThread;
        private object _locker = new object();

        /// <summary>
        /// Creates the Client and attempts to connect to the server
        /// </summary>
        /// <param name="servername">Name of the server/IP address to the server</param>
        /// <param name="port">The servers port number</param>
        /// <param name="formAuctionClient">Reference for the FormAuctionClient</param>
        public Client(string servername, int port, FormAuctionClient formAuctionClient)
        {
            this.formAuctionClient = formAuctionClient;
            this.servername = servername;
            this.port = port;
            auctions = new List<Auction>();
            ConnectToServer();
        }

        /// <summary>
        /// Connects to the server, or close the program if not possible
        /// </summary>
        private void ConnectToServer()
        {
            try
            {
                server = new TcpClient(servername, port);
                stream = server.GetStream();
                reader = new Reader(stream, this);
                writer = new StreamWriter(stream);
                readerThread = new Thread(reader.RunReader);
                readerThread.Start();
                myIP = server.Client.LocalEndPoint.ToString();
            }
            catch
            {
                if (MessageBox.Show("Kunne ikke logge på! \n(Annuller vil lukke programmet igen)", "Fejl", MessageBoxButtons.RetryCancel) == DialogResult.Retry)
                {
                    ConnectToServer();
                }
                else
                {
                    formAuctionClient.CloseApplication();
                }
            }
        }

        /// <summary>
        /// Reads data recieved from the server, and performs actions according to the message recieved
        /// </summary>
        /// <param name="readerData">Data from the server</param>
        public void ReaderData(string readerData)
        {
            lock (_locker)
            {
                string[] readerCommands = readerData.Split(';');
                Auction currentAuction;
                switch (readerCommands[0].ToLower())
                {
                    case "newauction":
                        auctions.Add(new Auction(int.Parse(readerCommands[1]), readerCommands[2], int.Parse(readerCommands[3]), int.Parse(readerCommands[4])));
                        formAuctionClient.UpdateAuctions();
                        break;
                    case "auctionclosed":
                        currentAuction = FindAuction(int.Parse(readerCommands[1]));
                        currentAuction.Bid = int.Parse(readerCommands[2]);
                        currentAuction.HammerTime = 3;
                        currentAuction.Winner = readerCommands[3];
                        string winner;
                        if (myIP == currentAuction.Winner) { winner = "dig"; } else { winner = currentAuction.Winner; }
                        formAuctionClient.UpdateAuctions();
                        formAuctionClient.FormMessage("Tredje. Solgt til " + winner + " for " + currentAuction.Bid + " kr.", Color.LightGreen, int.Parse(readerCommands[1]));
                        break;
                    case "auctionupdate":
                        currentAuction = FindAuction(int.Parse(readerCommands[1]));
                        currentAuction.HammerTime = int.Parse(readerCommands[2]);
                        formAuctionClient.UpdateAuctions();
                        if(currentAuction.HammerTime == 1)
                            formAuctionClient.FormMessage("Første.", Color.SkyBlue, int.Parse(readerCommands[1]));
                        else
                            formAuctionClient.FormMessage("Anden.", Color.SkyBlue, int.Parse(readerCommands[1]));
                        break;
                    case "newbid":
                        currentAuction = FindAuction(int.Parse(readerCommands[1]));
                        currentAuction.Bid = int.Parse(readerCommands[2]);
                        formAuctionClient.UpdateAuctions();
                        break;
                    case "bidaccepted":
                        formAuctionClient.FormMessage("Budet accepteret.", Color.LightGreen, int.Parse(readerCommands[1]));
                        break;
                    case "bidtoolow":
                        formAuctionClient.FormMessage("Budet er for lavt.", Color.IndianRed, int.Parse(readerCommands[1]));
                        break;
                    case "auctionnotexist":
                        currentAuction = FindAuction(int.Parse(readerCommands[1]));
                        auctions.Remove(currentAuction);
                        formAuctionClient.UpdateAuctions();
                        formAuctionClient.FormMessage("Genstanden kunne ikke findes, og er blevet fjernet fra listen.", Color.IndianRed, 0);
                        break;
                    case "serverclosing":
                        reader.CloseApplication();
                        MessageBox.Show("Forbindelsen til serveren er blevet lukket,\nog programmet lukker derfor ned.", "Forbindelse", MessageBoxButtons.OK);
                        formAuctionClient.CloseApplication();
                        break;
                    default:
                        break;
                }
            }
        }

        /// <summary>
        /// Bids on an auction
        /// </summary>
        /// <param name="id">ID of the auction</param>
        /// <param name="bid">Amount to bid</param>
        public void AuctionBid(int id, int bid)
        {
            writer.WriteLine("bid;" + id.ToString() + ";" + bid.ToString());
            writer.Flush();
        }

        /// <summary>
        /// Finds an auction
        /// </summary>
        /// <param name="id">ID of the auction</param>
        /// <returns>Auction object</returns>
        public Auction FindAuction(int id)
        {
            foreach (Auction currentAuction in auctions)
            {
                if (currentAuction.Id == id)
                    return currentAuction;
            }
            return null;
        }

        /// <summary>
        /// Get a list of auctions
        /// </summary>
        /// <returns>List with Auction objects</returns>
        public List<Auction> GetAuctionList()
        {
            return auctions;            
        }

        /// <summary>
        /// Sends closing message to the server and closes the application
        /// </summary>
        public void CloseApplication()
        {
            try
            {
                writer.WriteLine("closing");
                writer.Flush();
                writer.Close();
            }
            catch
            {
                // Do Nothing
            }
            reader.CloseApplication();
            readerThread.Join();
        }
    }
}
